package com.springboot.security.filter;

import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.authentication.InsufficientAuthenticationException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.stereotype.Component;

import java.util.Collection;

/**
 * 自定义的 CustomAccessDecisionManager 并重写 decide 方法，在该方法中判断当前登录用户是否具有当前请求 URL 所需的
 * 角色信息，如果不具备么就抛出异常 AccessDeniedException，否则不做任何事即可
 * @author wangning
 * @create 2021-05-21 11:49
 */
@Component
public class CustomAccessDecisionManager implements AccessDecisionManager {

	/**
	 * @param authentication   包含当前登录用户的信息
	 * @param object           是一个 FilterInvocation 对象，可以获取当前请求对象等
	 * @param configAttributes 是 FilterInvocationSecurityMetadataSource 中 getAttributes 方法的返回值， 即当前请求URL所需的角色
	 * @throws AccessDeniedException               异常
	 * @throws InsufficientAuthenticationException 异常
	 */
	@Override
	public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes) throws AccessDeniedException, InsufficientAuthenticationException {
		/*
		进行角色信息对比，如果需要的角色是 ROLE_LOGIN，说明当前请求的 URL 用户登录后即可访问，如果 authorization是
		UsernamePasswordAuthenticationToken 的实例，那么说明当前用户已登录，该方法到此结束，否则进入正常的判断流程，如果当前用户具备
		当前请求需要的角色，那么方法结束

		 */
		Collection<? extends GrantedAuthority> authorities = authentication.getAuthorities();
		for (ConfigAttribute configAttribute : configAttributes) {
			if ("ROLE_LOGIN".equals(configAttribute.getAttribute()) && authentication instanceof UsernamePasswordAuthenticationToken) {
				return;
			}
			for (GrantedAuthority authority : authorities) {
				if (configAttribute.getAttribute().equals(authority.getAuthority())) {
					return;
				}
			}
		}
		throw new AccessDeniedException("权限不足");
	}

	/**
	 *
	 * @param attribute
	 * @return
	 */
	@Override
	public boolean supports(ConfigAttribute attribute) {
		return true;
	}

	/**
	 *
	 * @param clazz
	 * @return
	 */
	@Override
	public boolean supports(Class<?> clazz) {
		return true;
	}
}
